package com.ys.web.user.friends.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import com.ys.base.BaseConstants;
import com.ys.base.EnumType;
import com.ys.utils.base.http.McodeHandler;
import com.ys.utils.base.j2se.*;
import com.ys.utils.help.GroupUtils;
import com.ys.utils.help.TipUtils;
import com.ys.utils.help.UserUtils;
import com.ys.utils.redis.Redis;
import com.ys.utils.redis.key.GroupKey;
import com.ys.utils.redis.key.UserKey;
import com.ys.utils.spring.base.TaskUtils;
import com.ys.utils.spring.config.SystemError;
import com.ys.utils.spring.properties.Config;
import com.ys.web.user.friends.mapper.GroupInfoMapper;
import com.ys.web.user.friends.mapper.UserFriendsMapper;
import com.ys.web.user.friends.mapper.UserGroupMapper;
import com.ys.web.user.friends.po.BaseGroupInfoPO;
import com.ys.web.user.friends.po.BaseUserFriendsPO;
import com.ys.web.user.friends.po.BaseUserGroupPO;
import com.ys.web.user.friends.service.GroupService;
import com.ys.web.user.friends.vo.*;
import com.ys.web.user.login.LoginInfo;
import com.ys.web.user.privacy.Privacy;
import com.ys.web.user.privacy.PrivacyManager;
import com.ys.web.ws.bean.MessageResponseBean;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.DefaultTypedTuple;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.jdbc.UncategorizedSQLException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author quxinjun
 * @version 1.0.0
 * @ClassName GroupServiceImpl.java
 * @Description 组服务实现类
 * @createTime 2021年05月13日 19:36:00
 */
@Slf4j
@Service
public class GroupServiceImpl implements GroupService {

    @Autowired
    private GroupInfoMapper groupInfoMapper;

    @Autowired
    private UserGroupMapper userGroupMapper;

    @Autowired
    private UserFriendsMapper userFriendsMapper;

    @Value("${ys.default.head_img}")
    private String DEFAULT_HEAD_IMG;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long addGroup(Long uid, String name, Set<String> memberss) {
        //查看当前群主有多少个群
        Integer canUseCount = StringHandler.getInt(Config.pp.getOrDefault(BaseConstants.DataSourceConstants.USER_CAN_USE_GROUP_COUNT, "3"));
        int count = groupInfoMapper.selectUserUse(uid);
        if ((count + 1) > canUseCount) {
            SystemError.wrapBs("500", "当前可创建的群最多" + canUseCount + "个");
        }

        //验证人数
        List<String> members = new ArrayList<>(memberss);
        int memberSize = StringHandler.getInt(Config.pp.getOrDefault(BaseConstants.DataSourceConstants.INIT_GROUP_SIZE, "400"));
        validateGroupMemberLimit(null, members.size(), memberSize);
        //过滤
        final List<String>[] finalMembers1 = new List[]{members};
        FutureTask<Map<Boolean, List<String>>> init = TaskUtils.run((t) -> {
            List<Privacy> privacyList = PrivacyManager.getPrivacyByIds(finalMembers1[0]);
            Map<Long, Privacy> privacyMap = privacyList.stream().collect(Collectors.toMap(Privacy::getUserId, Function.identity()));
            return finalMembers1[0].stream()
                    .filter(x -> privacyMap.get(McodeHandler.from32Radix(x)).getHideGroup() || privacyMap.get(McodeHandler.from32Radix(x)).getGroupVerify())
                    .collect(Collectors.groupingBy(x -> !privacyMap.get(McodeHandler.from32Radix(x)).getHideGroup()));
        });

        String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern(BaseConstants.BASE_FORMAT));
        //创建群信息
        BaseGroupInfoPO baseGroupInfoPO = new BaseGroupInfoPO();
        baseGroupInfoPO.setStatus(true);
        baseGroupInfoPO.setName(name);
        baseGroupInfoPO.setIsAudit(true);//是否需要审核
        baseGroupInfoPO.setIsBanned(false);//是否全员禁言
        baseGroupInfoPO.setIsMemberProtect(true);//是否成员保护
        baseGroupInfoPO.setPic(DEFAULT_HEAD_IMG);
        baseGroupInfoPO.setUserId(String.valueOf(uid));
        baseGroupInfoPO.setIsScreenNotice(false);//截屏通知
        baseGroupInfoPO.setDisturb(false);
        baseGroupInfoPO.setIsAdvertIntercept(false);//广告拦截
        baseGroupInfoPO.setIsFreeze(false);//封群字段
        baseGroupInfoPO.setGradCount(0);
        baseGroupInfoPO.setGradTime(0);
        baseGroupInfoPO.setMaxPerson(memberSize);
        baseGroupInfoPO.setCreateTime(time);
        groupInfoMapper.insertObj(baseGroupInfoPO);
        ThreadUtil.execAsync(() -> {
            this.setBaseGroupInfoCache(baseGroupInfoPO);
            //用户在创建群组的时候   如果这个时候缓存了用户的群组信息，需要及时更新 如果没有缓存  就不需要更新，等用户查询的时候就是了
            String userGroupKey = UserKey.groupInfoKey(uid);
            if (Redis.key.exists(userGroupKey)) {
                Redis.hash.hSet(userGroupKey, String.valueOf(baseGroupInfoPO.getId()), JsonUtil.toJson(baseGroupInfoPO));
            }

            String groupAssistantTokenKey = GroupKey.groupAssistantToken(baseGroupInfoPO.getId());
            Redis.str.set(groupAssistantTokenKey, IdUtil.randomUUID().replaceAll("-", "").toUpperCase());
        });

        //组装数据
        LoginInfo cacheUser = UserUtils.getCacheUser(uid);
        //添加群成员
        try {
            Map<Boolean, List<String>> pMap = init.get();
            if (StringHandler.isNotEmpty(pMap.get(true))) {
                members.removeAll(pMap.get(true));
            }
            if (StringHandler.isNotEmpty(pMap.get(false))) {
                members.removeAll(pMap.get(false));
            }
            MessageResponseBean response = new MessageResponseBean();

            response.setCode(EnumType.MessageCode.MESSAGE_GROUP_INVITE_USER_NEED_VALIDATE.getCode());
            response.setWindowId(BaseConstants.SINGLE_PREFIX + McodeHandler.to32Radix(uid));
            response.setCreateTime(time);
            response.setGroupId(String.valueOf(baseGroupInfoPO.getId()));
            response.setFrom(McodeHandler.to32Radix(uid));
            response.setAvatarUrl(cacheUser.getAvatarUrl());
            response.setUserName(cacheUser.getNickName());
            response.setMessageId(IdUtil.simpleUUID());
//            response.setGroupName(baseGroupInfoPO.getName() + "(" + (members.size() + 1) + ")");
            response.setGroupName(baseGroupInfoPO.getName());
            response.setGroupPic(baseGroupInfoPO.getPic());
            response.setData(GroupUtils.getInviteNotice(String.valueOf(baseGroupInfoPO.getId()), String.valueOf(uid)));
            ThreadUtil.execAsync(() -> pMap.get(true)
                    .forEach(o -> GroupUtils.sendIfAbsent(response, String.valueOf(McodeHandler.from32Radix(o)), BaseConstants.SINGLE_PREFIX + McodeHandler.to32Radix(uid))));
        } catch (Exception e) {
            e.printStackTrace();
        }
        //保存组成员信息
        members = members.stream().map(x -> String.valueOf(McodeHandler.from32Radix(x))).collect(Collectors.toList());
        members.add(String.valueOf(uid));
        String key = GroupKey.groupMembersKey(baseGroupInfoPO.getId());
        Set<ZSetOperations.TypedTuple<String>> tuples = new HashSet<>();
        members.forEach(obj -> {
            DefaultTypedTuple<String> defaultTypedTuple = new DefaultTypedTuple<>(obj, obj.equals(String.valueOf(uid)) ?
                    EnumType.GroupIdentity.MASTER.getCode().doubleValue() :
                    EnumType.GroupIdentity.MEMBERS.getCode().doubleValue());
            tuples.add(defaultTypedTuple);
        });

        userGroupMapper.insertListForAddGroup(tuples, uid, baseGroupInfoPO.getId());
        Redis.zSet.add(key, tuples);
        return baseGroupInfoPO.getId();
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delGroup(Long uid, String groupId) {
        String key = GroupKey.groupMembersKey(groupId);
        //删除组的时候  组的信息  在缓存中肯定存在
        if (Redis.key.exists(key)) {
            Set<String> members = Redis.zSet.range(key, 0, -1);
            String masterId = members.iterator().next();
            //判断操作人和获取的群主id是否一致
            if (String.valueOf(uid).equals(masterId)) {
                BaseGroupInfoPO baseGroupInfoPO = new BaseGroupInfoPO();
                baseGroupInfoPO.setUserId(String.valueOf(uid));
                baseGroupInfoPO.setStatus(true);
                baseGroupInfoPO.setId(Long.parseLong(groupId));
                BaseGroupInfoPO entity = groupInfoMapper.selectOne(baseGroupInfoPO);
                if (StringHandler.isNotEmpty(entity)) {
                    entity.setStatus(false);
                    int row = groupInfoMapper.deleteGroupInfo(entity.getId());
                    if (row > 0) {
                        userGroupMapper.deleteMembers(entity.getId());

                        List<String> ids = new ArrayList<>(members);
                        GroupUtils.sendIds(ids, uid, groupId, EnumType.MessageCode.MESSAGE_GROUP_DEL.getCode(), TipUtils.GROUP_DISSOLVE);

                        //删除缓存
                        Redis.key.del(key);
                        Redis.key.del(GroupKey.groupInfoKey(groupId));
                        Redis.key.del(GroupKey.groupAssistantToken(groupId));

                        ThreadUtil.execAsync(() -> {
                            ids.forEach(o -> {
                                String userGroupKey = UserKey.groupInfoKey(o);
                                if (Redis.key.exists(userGroupKey)) {
                                    Redis.hash.delHash(UserKey.groupInfoKey(o), groupId);
                                }
                            });
                        });
                        return;
                    }
                }
            } else {
                SystemError.wrapBs(EnumType.ResponseCode.Fail.getCode(), "您没有操作权限");
            }
        }
        SystemError.wrapBs(EnumType.ResponseCode.Fail.getCode(), EnumType.ResponseCode.Fail.getDesc());

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void turnGroup(Long uid, String groupId, String memberId) {
        String key = GroupKey.groupMembersKey(groupId);
        //删除组的时候  组的信息  在缓存中肯定存在
        if (Redis.key.exists(key)) {
            Set<String> members = Redis.zSet.range(key, 0, -1);
            String masterId = members.iterator().next();
            //判断操作人和获取的群主id是否一致
            if (String.valueOf(uid).equals(masterId)) {
                BaseGroupInfoPO baseGroupInfoPO = new BaseGroupInfoPO();
                baseGroupInfoPO.setUserId(String.valueOf(uid));
                baseGroupInfoPO.setStatus(true);
                baseGroupInfoPO.setId(Long.parseLong(groupId));
                BaseGroupInfoPO entity = groupInfoMapper.selectOne(baseGroupInfoPO);
                if (StringHandler.isNotEmpty(entity)) {
                    entity.setUserId(String.valueOf(McodeHandler.from32Radix(memberId)));
                    //修改群基本信息中的群主id
                    groupInfoMapper.updateByPrimaryKeySelective(entity);
                    BaseUserGroupPO q1 = new BaseUserGroupPO();
                    q1.setUserId(String.valueOf(uid));
                    q1.setGroupId(Long.parseLong(groupId));
                    q1.setStatus(1);
                    BaseUserGroupPO o1 = userGroupMapper.selectOne(q1);
                    BaseUserGroupPO q2 = new BaseUserGroupPO();
                    q2.setUserId(String.valueOf(McodeHandler.from32Radix(memberId)));
                    q2.setGroupId(Long.parseLong(groupId));
                    q2.setStatus(1);
                    BaseUserGroupPO o2 = userGroupMapper.selectOne(q2);
                    if (StringHandler.isEmpty(o1) || StringHandler.isEmpty(o2)) {
                        //如果其中一个数据不存在  数据存在问题   回滚事务
                        throw new RuntimeException();
                    }
                    Date date = new Date();
                    o1.setUpdateTime(date);
                    o1.setUserIdentity(EnumType.GroupIdentity.MEMBERS.getCode());
                    o2.setUpdateTime(date);
                    o2.setUserIdentity(EnumType.GroupIdentity.MASTER.getCode());
                    int r1 = userGroupMapper.updateByPrimaryKeySelective(o1);
                    int r2 = userGroupMapper.updateByPrimaryKeySelective(o2);
                    if (r1 > 0 && r2 > 0) {
                        //修改缓存
                        Redis.zSet.add(key, o1.getUserId(), o1.getUserIdentity());
                        Redis.zSet.add(key, o2.getUserId(), o2.getUserIdentity());
                        GroupUtils.sendIds(new ArrayList<>(members), uid, groupId, EnumType.MessageCode.MESSAGE_GROUP_TURN.getCode(), TipUtils.turnGroupTip(groupId, String.valueOf(uid)));
                        return;
                    } else {
                        //回滚事务
                        throw new RuntimeException();
                    }
                }
            } else {
                SystemError.wrapBs(EnumType.ResponseCode.Fail.getCode(), "您没有操作权限");
            }
        }
        SystemError.wrapBs(EnumType.ResponseCode.Fail.getCode(), EnumType.ResponseCode.Fail.getDesc());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delMemberByMemberId(Long uid, String memberId, String groupId) {
        List<String> ids = Arrays.asList(memberId.split(","));

        //判断操作人 是不是群主或者管理员
        String key = GroupKey.groupMembersKey(groupId);
        GroupUtils.validateIsManager(uid, groupId);
        Set<String> members = Redis.zSet.range(key, 0, -1);

        //如果被删除的成员包含群主
        if (ids.contains(McodeHandler.to32Radix(Long.parseLong(members.iterator().next())))) {
            SystemError.wrapBs(EnumType.ResponseCode.Fail.getCode(), "不能将群主踢出改群，操作失败");
        }

        int row = userGroupMapper.delMemberById(groupId, ids.stream().map(McodeHandler::from32Radix).collect(Collectors.toList()));

        if (row == ids.size()) {
            ThreadUtil.execAsync(() -> {
                //判断删除的成员id 是不是在该群中
                ids.forEach(obj -> {
                    long delId = McodeHandler.from32Radix(obj);
                    if (!members.contains(String.valueOf(delId))) {
                        SystemError.wrapBs(EnumType.ResponseCode.Fail.getCode(), "数据出错");
                    }

                    Redis.zSet.remove(key, String.valueOf(delId));
                    Redis.hash.delHash(UserKey.groupInfoKey(delId), groupId);

                    //过滤移除的成员
                    members.remove(String.valueOf(delId));
                    GroupUtils.sendIds(new ArrayList<>(Collections.singletonList(String.valueOf(delId))), uid, groupId, EnumType.MessageCode.MESSAGE_GROUP_DEL_MEMBER.getCode(),
                            TipUtils.beRemovedTip(groupId, String.valueOf(uid)));
                    //发送消息给群成员
                    GroupUtils.sendIds(new ArrayList<>(members), uid, groupId, EnumType.MessageCode.MESSAGE_GROUP_DEL_MEMBER.getCode(),
                            TipUtils.moveGroup(groupId, String.valueOf(uid), String.valueOf(delId)));
                });
            });
        } else {
            SystemError.wrapBs(EnumType.ResponseCode.Fail.getCode(), "操作失败");
        }
    }

    @Override
    public List<BaseUserFriendsPO> getCanInviteList(Long uid, String groupId) {
        String key = GroupKey.groupMembersKey(groupId);
        //获取组成员信息
        Set<String> members = Redis.zSet.range(key, 0, -1);
        String userFriendKey = UserKey.getUserFriendsListKey(uid);
        Map friendsMap = Redis.hash.hgetall(userFriendKey);
        List<String> list = new ArrayList<String>(friendsMap.values());
        //组成员列表中不包含自己列表中的朋友id的朋友信息
        List<BaseUserFriendsPO> collect = list.stream()
                .map(o -> JsonUtil.jsonToBean(o, BaseUserFriendsPO.class))
                .filter(o -> StringHandler.isNotEmpty(o)
                        && StringHandler.isNotEmpty(o.getId())
                        && !members.contains(o.getFriendId()))
                .filter(x -> !PrivacyManager.hideGroup(x.getFriendId()))
                .map(o -> {
                    o.setUserId(McodeHandler.to32Radix(Long.parseLong(o.getUserId())));
                    o.setFriendId(McodeHandler.to32Radix(Long.parseLong(o.getFriendId())));
                    return o;
                }).collect(Collectors.toList());
        return collect;
    }

    @Override
    public List<UserGroupVO> getCanDeleteList(Long uid, String groupId) {
        String key = GroupKey.groupMembersKey(groupId);
        //获取组成员信息
        Set<String> members = Redis.zSet.range(key, 0, -1);
        //移除自己
        members.remove(String.valueOf(uid));
        if (members.isEmpty()) {
            return new ArrayList<>();
        }
        //获取成员基本属性
        List<UserGroupVO> userGroupList = userGroupMapper.findUserGroupBaseInfoList(groupId, new ArrayList<>(members), uid);
        //获取好友基本属性备注名称
//        List<BaseUserFriendsPO> result = userFriendsMapper.findFriendsRemarkByUid(uid);
//        Map<String, BaseUserFriendsPO> friendsListMap = result.stream().collect(Collectors.toMap(o -> String.valueOf(o.getId()), Function.identity()));
        userGroupList.stream().filter(o -> StringHandler.isNotEmpty(o.getUserId())).forEach(o -> {
//            if (!friendsListMap.isEmpty()) {
            //存放好友的备注名称
//                o.setRemarkName(friendsListMap.get(o.getUserId()) != null ? friendsListMap.get(o.getUserId()).getFriendRemark() : null);
            o.setUserId(McodeHandler.to32Radix(Long.parseLong(o.getUserId())));
//            }
        });
        return userGroupList;
    }

    @Override
    public UserGroupInfoVO getGroupDetail(Long uid, String groupId) {
        //根据组id  获取组成员 id 集合
        String key = GroupKey.groupMembersKey(groupId);
        Set<String> members = Redis.zSet.range(key, 0, -1);

        //组的基础信息
        String groupJsonInfo = Redis.str.get(GroupKey.groupInfoKey(groupId));
        if (StringHandler.isEmpty(groupJsonInfo) || StringHandler.isEmpty(members) || members.isEmpty()) {
            SystemError.wrapBs(EnumType.ResponseCode.Fail.getCode(), "当前组不存在");
        }
        BaseGroupInfoPO baseGroupInfoPO = JsonUtil.jsonToBean(groupJsonInfo, BaseGroupInfoPO.class);

        //设置基础信息
        UserGroupInfoVO userGroupInfoVO = new UserGroupInfoVO();

        userGroupInfoVO.setGroupNum(McodeHandler.to32Radix(baseGroupInfoPO.getId()));
        userGroupInfoVO.setName(baseGroupInfoPO.getName());
        userGroupInfoVO.setPic(baseGroupInfoPO.getPic());
        userGroupInfoVO.setIsAudit(baseGroupInfoPO.getIsAudit());
        userGroupInfoVO.setIsBanned(baseGroupInfoPO.getIsBanned());
        userGroupInfoVO.setIsMemberProtect(baseGroupInfoPO.getIsMemberProtect());
        userGroupInfoVO.setIsScreenNotice(baseGroupInfoPO.getIsScreenNotice());
        userGroupInfoVO.setIsAdvertIntercept(baseGroupInfoPO.getIsAdvertIntercept());
        userGroupInfoVO.setClearSetStr(EnumType.MessageClearCode.getDescByCode(baseGroupInfoPO.getClearSetCode()));
        userGroupInfoVO.setGradCount(baseGroupInfoPO.getGradCount() == null ? 0 : baseGroupInfoPO.getGradCount());
        userGroupInfoVO.setGradTime(baseGroupInfoPO.getGradTime() == null ? 0 : baseGroupInfoPO.getGradTime() / 60);
        userGroupInfoVO.setIsGradAll(baseGroupInfoPO.getIsGradAll() != null && baseGroupInfoPO.getIsGradAll());
        userGroupInfoVO.setNotice(baseGroupInfoPO.getNotice());


        Map<Object, Object> remarkMap = Redis.hash.hgetall(GroupKey.groupUserGroupRemarkKey(groupId));
        if (remarkMap != null && !remarkMap.isEmpty()) {
            //设置用户在群中的名称
            userGroupInfoVO.setUserGroupRemark(remarkMap.get(String.valueOf(uid)) != null ? String.valueOf(remarkMap.get(String.valueOf(uid))) : null);
        }
        //设置群中用户的基本信息
        //获取成员基本属性
        List<UserGroupVO> userGroupList = userGroupMapper.findUserGroupBaseInfoList(groupId, new ArrayList<>(members), uid);
        //获取好友基本属性备注名称

        userGroupList.forEach(o -> {
            //存放当前用户的身份信息
            if (o.getUserId().equals(String.valueOf(uid))) {
                userGroupInfoVO.setUserIdentity(o.getUserIdentity());
            }

            o.setUserId(McodeHandler.to32Radix(Long.parseLong(o.getUserId())));
            if (StringHandler.isEmpty(o.getAvatarUrl())) {
                o.setAvatarUrl(DEFAULT_HEAD_IMG);
            }
        });

        userGroupInfoVO.setMembers(userGroupList.stream().sorted(Comparator.comparing(UserGroupVO::getUserIdentity)).collect(Collectors.toList()));
        return userGroupInfoVO;
    }

    @Override
    public BaseGroupInfoPO getGroupBaseInfo(Long uid, String groupId) {
        BaseGroupInfoPO baseGroupInfoPO = getGroupBaseInfoByCache(groupId);
        baseGroupInfoPO.setMemberSize(Redis.key.exists(GroupKey.groupMembersKey(groupId)) ? Redis.zSet.size(GroupKey.groupMembersKey(groupId)).intValue() : 0);
        baseGroupInfoPO.setUserId(McodeHandler.to32Radix(Long.parseLong(baseGroupInfoPO.getUserId())));
        //判断用户是否在群内
        Double rank = Redis.zSet.score(GroupKey.groupMembersKey(groupId), String.valueOf(uid));
        baseGroupInfoPO.setIsJoin(StringHandler.isNotEmpty(rank) && rank != 0.0);
        return baseGroupInfoPO;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void setGroupBase(Long uid, String groupId, Integer type, String value) {
/*
        0.设置群昵称
        1.设置群头像
        2.设置群名称
        3.设置群公告
        4.设置群是否需要审核
        5.设置全员禁言
        6.设置成员保护模式
        7.设置定时
        8.设置截屏通知
        9.广告拦截
        10.禁止全员抢红包
         */
        //判断用户的权限
        if (!type.equals(0)) {
            GroupUtils.validateIsManager(uid, groupId);
        }
        Set<String> members = Redis.zSet.range(GroupKey.groupMembersKey(groupId), 0, -1);
        //移除当前操作用户
        List<String> ids = new ArrayList<>(members);
        switch (type) {
            case 0:
                try {
                    userGroupMapper.updateUserGroupRemarkName(uid, groupId, value);
                } catch (UncategorizedSQLException e) {
                    SystemError.wrapBs(EnumType.ResponseCode.Fail.getCode(), "名称不合法");
                }
                //设置该群用户备注信息
                Redis.hash.hSet(GroupKey.groupUserGroupRemarkKey(groupId), String.valueOf(uid), value);
                Redis.key.setKeyTimeout(GroupKey.groupUserGroupRemarkKey(groupId), BaseConstants.SAVE_TIME);
                ThreadUtil.execAsync(() -> GroupUtils.sendIds(ids, uid, groupId, EnumType.MessageCode.MESSAGE_GROUP_SET_REMARK.getCode(),
                        TipUtils.setGroupBase(groupId, String.valueOf(uid), value, type))
                );
                break;
            case 1: {
                BaseGroupInfoPO baseGroupInfoPO = groupInfoMapper.selectByPrimaryKey(groupId);
                baseGroupInfoPO.setPic(value);
                groupInfoMapper.updateByPrimaryKeySelective(baseGroupInfoPO);
                this.setBaseGroupInfoCache(baseGroupInfoPO);
                break;
            }
            case 2: {
                BaseGroupInfoPO baseGroupInfoPO = groupInfoMapper.selectByPrimaryKey(groupId);
                baseGroupInfoPO.setName(value);
                try {
                    groupInfoMapper.updateByPrimaryKeySelective(baseGroupInfoPO);
                } catch (UncategorizedSQLException e) {
                    SystemError.wrapBs(EnumType.ResponseCode.Fail.getCode(), "名称不合法");
                }
                this.setBaseGroupInfoCache(baseGroupInfoPO);

                ThreadUtil.execAsync(() -> GroupUtils.sendIds(ids, uid, groupId, EnumType.MessageCode.MESSAGE_GROUP_UPDATE_NAME.getCode(),
                        TipUtils.setGroupBase(groupId, String.valueOf(uid), value, type))
                );
                break;
            }
            case 3: {
                BaseGroupInfoPO baseGroupInfoPO = groupInfoMapper.selectByPrimaryKey(groupId);
                baseGroupInfoPO.setNotice(value);
                groupInfoMapper.updateByPrimaryKeySelective(baseGroupInfoPO);
                this.setBaseGroupInfoCache(baseGroupInfoPO);
//                ThreadUtil.execAsync(() -> GroupUtils.sendIds(ids, uid, groupId, EnumType.MessageCode.MESSAGE_GROUP_UPDATE_NOTICE.getCode(),
//                        TipUtils.setGroupBase(groupId, String.valueOf(uid), value, type))
//                );
                ThreadUtil.execAsync(() -> GroupUtils.sendIds(ids, uid, groupId, EnumType.MessageCode.MESSAGE_GROUP_UPDATE_NOTICE.getCode(),
                        JsonUtil.toJson(MapHandler.initMap("tip", TipUtils.setGroupBase(groupId, String.valueOf(uid), value, type), "content",
                                value.replaceAll("/r/n", "    ").replaceAll("/n", "    "))))
                );
                break;
            }
            case 4: {
                BaseGroupInfoPO baseGroupInfoPO = groupInfoMapper.selectByPrimaryKey(groupId);
                //取反
                baseGroupInfoPO.setIsAudit(!baseGroupInfoPO.getIsAudit());
                groupInfoMapper.updateByPrimaryKeySelective(baseGroupInfoPO);
                this.setBaseGroupInfoCache(baseGroupInfoPO);
                ThreadUtil.execAsync(() -> {
                            GroupUtils.sendIds(ids, uid, groupId, EnumType.MessageCode.MESSAGE_GROUP_SET_IS_AUDIT.getCode(),
                                    TipUtils.setGroupBase(groupId, String.valueOf(uid), baseGroupInfoPO.getIsAudit(), type));
                            if (!baseGroupInfoPO.getIsAudit()) {
                                //删除未审核的成员信息
                                userGroupMapper.delUnAuditRow(groupId);
                            }
                        }
                );
                break;
            }
            case 5: {
                BaseGroupInfoPO baseGroupInfoPO = groupInfoMapper.selectByPrimaryKey(groupId);
                baseGroupInfoPO.setIsBanned(!baseGroupInfoPO.getIsBanned());
                groupInfoMapper.updateByPrimaryKeySelective(baseGroupInfoPO);
                this.setBaseGroupInfoCache(baseGroupInfoPO);
                ThreadUtil.execAsync(() -> GroupUtils.sendIds(ids, uid, groupId, EnumType.MessageCode.MESSAGE_GROUP_ALL_BANNED.getCode(),
                        TipUtils.setGroupBase(groupId, String.valueOf(uid), baseGroupInfoPO.getIsBanned(), type))
                );
                break;
            }
            case 6: {
                BaseGroupInfoPO baseGroupInfoPO = groupInfoMapper.selectByPrimaryKey(groupId);
                baseGroupInfoPO.setIsMemberProtect(!baseGroupInfoPO.getIsMemberProtect());
                groupInfoMapper.updateByPrimaryKeySelective(baseGroupInfoPO);
                this.setBaseGroupInfoCache(baseGroupInfoPO);
                ThreadUtil.execAsync(() ->
                        GroupUtils.sendIds(ids, uid, groupId, EnumType.MessageCode.MESSAGE_GROUP_SET_MEMBER_PROTECT.getCode(),
                                TipUtils.setGroupBase(groupId, String.valueOf(uid), baseGroupInfoPO.getIsMemberProtect(), type))
                );
                break;
            }
            case 7: {
                BaseGroupInfoPO baseGroupInfoPO = groupInfoMapper.selectByPrimaryKey(groupId);
                baseGroupInfoPO.setClearSetTime(System.currentTimeMillis() / 1000);
                baseGroupInfoPO.setClearSetCode(Integer.parseInt(value));
                groupInfoMapper.updateByPrimaryKeySelective(baseGroupInfoPO);
                this.setBaseGroupInfoCache(baseGroupInfoPO);
                break;
            }
            case 8: {
                GroupUtils.validateIsMaster(uid, groupId);
                BaseGroupInfoPO baseGroupInfoPO = groupInfoMapper.selectByPrimaryKey(groupId);
                baseGroupInfoPO.setIsScreenNotice(!baseGroupInfoPO.getIsScreenNotice());
                groupInfoMapper.updateByPrimaryKeySelective(baseGroupInfoPO);
                this.setBaseGroupInfoCache(baseGroupInfoPO);
                GroupUtils.sendIds(ids.stream().filter(x -> !x.equals(String.valueOf(uid))).collect(Collectors.toList()), uid, groupId,
                        EnumType.MessageCode.MESSAGE_SCREEN_HOST_NOTICE.getCode(), TipUtils.setGroupBase(groupId, String.valueOf(uid), baseGroupInfoPO.getIsScreenNotice(), type));
                GroupUtils.sendIds(new ArrayList<>(Collections.singletonList(String.valueOf(uid))), uid, groupId,
                        EnumType.MessageCode.MESSAGE_SCREEN_HOST_NOTICE.getCode(), baseGroupInfoPO.getIsScreenNotice() ? TipUtils.SET_SCREEN_NOTICE : TipUtils.SET_SCREEN_NOTICE_NON);
                break;
            }
            case 9: {
//                GroupUtils.validateIsMaster(uid, groupId);
                BaseGroupInfoPO baseGroupInfoPO = groupInfoMapper.selectByPrimaryKey(groupId);
                baseGroupInfoPO.setIsAdvertIntercept(!baseGroupInfoPO.getIsAdvertIntercept());
                groupInfoMapper.updateByPrimaryKeySelective(baseGroupInfoPO);
                this.setBaseGroupInfoCache(baseGroupInfoPO);
                ThreadUtil.execAsync(() -> GroupUtils.sendIds(ids, uid, groupId,
                        EnumType.MessageCode.MESSAGE_SET_ADVERT_INTERCEPT.getCode(), TipUtils.setGroupBase(groupId, String.valueOf(uid), baseGroupInfoPO.getIsAdvertIntercept(), type)));
                break;
            }
            case 10: {
//                sw.start("全员禁止抢红包");
                GroupUtils.validateIsManager(uid, groupId);
                BaseGroupInfoPO baseGroupInfoPO = groupInfoMapper.selectByPrimaryKey(groupId);
                baseGroupInfoPO.setIsGradAll(!baseGroupInfoPO.getIsGradAll());
                groupInfoMapper.updateByPrimaryKeySelective(baseGroupInfoPO);
                this.setBaseGroupInfoCache(baseGroupInfoPO);
                ThreadUtil.execAsync(() -> GroupUtils.sendIds(ids, uid, groupId, EnumType.MessageCode.MESSAGE_GROUP_SET_GRAD_ALL.getCode(), TipUtils.setGroupBase(groupId, String.valueOf(uid), baseGroupInfoPO.getIsGradAll(), type)));
//                sw.stop();
                break;
            }
            default:
                break;
        }

    }

    @Override
    public synchronized void addGroupMember(Long uid, String groupId, String memberId) {
        //验证人数
        List<String> members = new ArrayList<>(new HashSet<>(Arrays.stream(memberId.split(",")).collect(Collectors.toList())));
        BaseGroupInfoPO cacheGroupInfo = GroupUtils.getCacheGroupInfo(groupId);
        validateGroupMemberLimit(groupId, members.size(), cacheGroupInfo.getMaxPerson());
        if (Redis.key.exists(GroupKey.groupMembersKey(groupId))) {
            Set<String> range = Redis.zSet.range(GroupKey.groupMembersKey(groupId), 0, -1);
            members.forEach(x -> {
                String s = StringHandler.getString(McodeHandler.from32Radix(x));
                if (range.contains(s)) {
                    String o = (String) Redis.hash.hGet(UserKey.getUserFriendsListKey(uid), s);
                    if (o != null) {
                        BaseUserFriendsPO baseUserFriendsPO = JsonUtil.jsonToBean(o, BaseUserFriendsPO.class);
                        SystemError.wrapBs("500", (baseUserFriendsPO.getFriendRemark() != null ? baseUserFriendsPO.getFriendRemark() : baseUserFriendsPO.getNickName()) + " 已加入当前群");
                    }
                }
            });
        } else {
            SystemError.wrapBs("500", "数据出错，请联系客服");
        }
        //获取到群管理的id
        String key = GroupKey.groupMembersKey(groupId);
        Set<String> manager = Redis.zSet.rangeByScore(key, EnumType.GroupIdentity.MASTER.getCode().doubleValue(), EnumType.GroupIdentity.MANAGER.getCode().doubleValue());
        Date date = new Date();
        LoginInfo cacheUser = UserUtils.getCacheUser(uid);

        //过滤
        final List<String>[] finalMembers1 = new List[]{members};
        FutureTask<Map<Boolean, List<String>>> init = TaskUtils.run((t) -> {
            List<Privacy> privacyList = PrivacyManager.getPrivacyByIds(finalMembers1[0]);
            Map<Long, Privacy> privacyMap = privacyList.stream().collect(Collectors.toMap(Privacy::getUserId, Function.identity()));
            Map<Boolean, List<String>> collect = finalMembers1[0].stream()
                    .filter(x -> privacyMap.get(McodeHandler.from32Radix(x)).getHideGroup() || privacyMap.get(McodeHandler.from32Radix(x)).getGroupVerify())
                    .collect(Collectors.groupingBy(x -> !privacyMap.get(McodeHandler.from32Radix(x)).getHideGroup()));
            return collect;
        });

        try {
            Map<Boolean, List<String>> pMap = null;
            pMap = init.get();
            if (StringHandler.isNotEmpty(pMap.get(true))) {
                members.removeAll(pMap.get(true));
            }
            if (StringHandler.isNotEmpty(pMap.get(false))) {
                members.removeAll(pMap.get(false));
            }
            //组装数据
            MessageResponseBean responseBean = new MessageResponseBean();
            responseBean.setCode(EnumType.MessageCode.MESSAGE_GROUP_INVITE_USER_NEED_VALIDATE.getCode());
            responseBean.setWindowId(BaseConstants.SINGLE_PREFIX + McodeHandler.to32Radix(uid));
            responseBean.setCreateTime(DateUtil.getDate(date, BaseConstants.BASE_FORMAT));
            responseBean.setFrom(McodeHandler.to32Radix(uid));
            responseBean.setAvatarUrl(cacheUser.getAvatarUrl());
            responseBean.setUserName(cacheUser.getNickName());
            responseBean.setMessageId(IdUtil.simpleUUID());
            responseBean.setGroupName(UserUtils.getGroupCacheNameByGroupId(groupId));
            responseBean.setGroupPic(cacheGroupInfo.getPic());
            responseBean.setGroupId(groupId);
            responseBean.setData(GroupUtils.getInviteNotice(groupId, String.valueOf(uid)));
            Map<Boolean, List<String>> finalPMap = pMap;
            ThreadUtil.execAsync(() -> {
                finalPMap.get(true).forEach(x ->
                        GroupUtils.sendIfAbsent(responseBean, String.valueOf(McodeHandler.from32Radix(x)), BaseConstants.SINGLE_PREFIX + McodeHandler.to32Radix(uid))
                );
            });
        } catch (Exception e) {
            e.printStackTrace();
        }


        //如果说不是管理员  则需要去判断是否开启了入群申请功能
        if (!manager.contains(String.valueOf(uid))) {
            //如果说需要审核  则添加基础信息   不添加审核信息
            if (cacheGroupInfo.getIsAudit()) {
                members.forEach(obj -> {
                    BaseUserGroupPO userGroupPO = new BaseUserGroupPO();
                    userGroupPO.setUserId(String.valueOf(McodeHandler.from32Radix(obj)));
                    userGroupPO.setGroupId(Long.parseLong(groupId));
                    userGroupPO.setStatus(1);
                    userGroupPO.setCreateTime(date);
                    userGroupPO.setUserIdentity(EnumType.GroupIdentity.MEMBERS.getCode());
                    userGroupPO.setInviteId(String.valueOf(uid));
                    userGroupMapper.insertSelective(userGroupPO);
                });
                if (!members.isEmpty()) {
                    //通知群主，管理员审核
                    //组装数据
                    MessageResponseBean response2 = UserUtils.getMessageResponse(String.valueOf(uid),
                            groupId, EnumType.MessageCode.MESSAGE_GROUP_ADD_MEMBER.getCode(),
                            "有新人入群需审核  去设置", DateUtil.getDate(date, BaseConstants.BASE_FORMAT), 0.0, 0.0);
                    manager.forEach(obj -> GroupUtils.sendIfAbsent(response2, obj, BaseConstants.GROUP_PREFIX + groupId));
                }
                return;
            }
        }
        List<String> memberIds = new ArrayList<>();
        members.forEach(obj -> {
            String id = String.valueOf(McodeHandler.from32Radix(obj));
            userGroupMapper.delObject(id, groupId);
            BaseUserGroupPO userGroupPO = new BaseUserGroupPO();
            userGroupPO.setUserId(id);
            userGroupPO.setGroupId(Long.parseLong(groupId));
            userGroupPO.setCreateTime(date);
            userGroupPO.setStatus(1);
            userGroupPO.setUserIdentity(EnumType.GroupIdentity.MEMBERS.getCode());
            userGroupPO.setInviteId(String.valueOf(uid));
            userGroupPO.setAuditId(String.valueOf(uid));
            userGroupPO.setAuditStatus(1);
            userGroupPO.setAuditTime(DateUtil.getDate(date, BaseConstants.BASE_FORMAT));
            userGroupMapper.insertSelective(userGroupPO);
            memberIds.add(id);
        });
        if (!memberIds.isEmpty()) {
            ThreadUtil.execAsync(() -> {
                this.saveGroupMember(groupId, uid, uid, memberIds, EnumType.MessageCode.MESSAGE_GROUP_INVITE_USER.getCode(), 1);
            });
        }
    }

    @Override
    public List<BaseGroupInfoPO> getGroupList(Long uid, String query) {
        String key = UserKey.groupInfoKey(uid);
        //判断key是否存在
        if (Redis.key.exists(key)) {
            Map map = Redis.hash.hgetall(key);
            if (map != null && !map.isEmpty()) {
                //延长5分钟
                Redis.key.setKeyTimeout(key, BaseConstants.GroupConstants.SAVE_TIME_USER_GROUP);
                ArrayList<String> list = new ArrayList<String>(map.values());
                List<BaseGroupInfoPO> result = list.stream().map(o -> {
                    BaseGroupInfoPO baseGroupInfoPO = JsonUtil.jsonToBean(o, BaseGroupInfoPO.class);
                    if (StringHandler.isNotEmpty(baseGroupInfoPO.getUserId())) {
                        baseGroupInfoPO.setUserId(McodeHandler.to32Radix(Long.parseLong(baseGroupInfoPO.getUserId())));
                    }
                    return baseGroupInfoPO;
                }).collect(Collectors.toList());
                if (StringHandler.isNotEmpty(query)) {
                    return result.stream().filter(o -> o.getName().contains(query)).collect(Collectors.toList());
                }
                return result;
            }
        } else {
            List<BaseGroupInfoPO> userGroupList = groupInfoMapper.findUserGroupListById(uid);
            if (!userGroupList.isEmpty()) {
                //保存缓存
                Map<String, String> collect = userGroupList
                        .stream()
                        .collect(Collectors.toMap(o -> String.valueOf(o.getId()), JsonUtil::toJson));
                Redis.hash.hmset(key, collect);
                Redis.key.setKeyTimeout(key, BaseConstants.GroupConstants.SAVE_TIME_USER_GROUP);
                return userGroupList.stream().map(o -> {
                    if (StringHandler.isNotEmpty(o.getUserId())) {
                        o.setUserId(McodeHandler.to32Radix(Long.parseLong(o.getUserId())));
                    }
                    return o;
                }).collect(Collectors.toList());
            }
        }
        return new ArrayList<>();
    }

    @Override
    public int getGroupMembersSize(String groupId) {
        Set<String> range = Redis.zSet.range(GroupKey.groupMembersKey(groupId), 0, -1);
        return range != null && !range.isEmpty() ? range.size() : 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized void audit(String groupId, Long uid, String memberId, Integer type) {
        GroupUtils.validateIsManager(uid, groupId);
        List<String> list = Arrays.asList(memberId.split(","));
        //验证集合中的用户是不是已经加入群聊
        if (Redis.key.exists(GroupKey.groupMembersKey(groupId))) {
            Set<String> range = Redis.zSet.range(GroupKey.groupMembersKey(groupId), 0, -1);
            list.forEach(x -> {
                String s = StringHandler.getString(McodeHandler.from32Radix(x));
                if (range.contains(s)) {
                    LoginInfo cacheUser = UserUtils.getCacheUser(s);
                    if (cacheUser != null) {
                        SystemError.wrapBs("500", (cacheUser.getNickName()) + " 已加入当前群");
                    }
                }
            });
        } else {
            SystemError.wrapBs("500", "数据出错，请联系客服");
        }

        //验证人数
        BaseGroupInfoPO cacheGroupInfo = GroupUtils.getCacheGroupInfo(groupId);
        validateGroupMemberLimit(groupId, list.size(), cacheGroupInfo.getMaxPerson());
        //解析id
        List<String> memberIds = list.stream().map(o -> String.valueOf(McodeHandler.from32Radix(o))).collect(Collectors.toList());
        //查询需要修改的 user_group 表的id
        List<BaseUserGroupPO> userList = userGroupMapper.findUserLastObject(groupId, memberIds);
        List<Long> collect = userList.stream().map(BaseUserGroupPO::getId).collect(Collectors.toList());
        //修改指定id的值
        userGroupMapper.updateAuditStatusByType(groupId, uid, collect, type == 1 ? 1 : 0);
        //删除多余 未被审核 的数据
        userGroupMapper.deleteOtherObject(groupId, memberIds);
        if (!type.equals(1)) {
            return;
        }
        ThreadUtil.execAsync(() -> {
            this.saveGroupMember(groupId, uid, null, memberIds, EnumType.MessageCode.MESSAGE_GROUP_AGREE_IN.getCode(), type);
        });
    }

    /**
     * @param groupId   组id
     * @param uid       操作人id
     * @param inviteId  邀请人id
     * @param memberIds 成员id
     * @title saveGroupMember
     * @description 保存组成员信息
     * @author quxinjun
     * @updateTime 2021/5/19 13:47
     */
    private void saveGroupMember(String groupId, Long uid, Long inviteId, List<String> memberIds, Integer code, Integer type) {
        Set<ZSetOperations.TypedTuple<String>> tuples = new HashSet<>();
        AtomicReference<String> memberNames = new AtomicReference<>("");
        memberIds.forEach(obj -> {
            DefaultTypedTuple<String> defaultTypedTuple = new DefaultTypedTuple<>(obj, EnumType.GroupIdentity.MEMBERS.getCode().doubleValue());
            tuples.add(defaultTypedTuple);
            memberNames.updateAndGet(v -> v +
                    UserUtils.getCacheUser(obj).getNickName() + ",");
        });

        Redis.zSet.add(GroupKey.groupMembersKey(groupId), tuples);
//        Redis.key.setKeyTimeout(GroupKey.groupMembersKey(groupId), BaseConstants.SAVE_TIME);

        Set<String> members = Redis.zSet.range(GroupKey.groupMembersKey(groupId), 0, -1);
//        members.remove(String.valueOf(uid));

        String data = memberNames.get().substring(0, memberNames.get().length() - 1) + " 加入群聊";
        if (StringHandler.isEmpty(inviteId)) {
            data = UserUtils.getCacheUser(uid).getNickName() + " 同意 " + data;
        } else {
            data = UserUtils.getCacheUser(uid).getNickName() + " 邀请 " + data;
        }
        String finalData = data;
        GroupUtils.sendIds(new ArrayList<>(members), uid, groupId, code, finalData);
    }

    /**
     * @param baseGroupInfoPO
     * @title setBaseGroupInfoCache
     * @description 设置群的基础信息缓存
     * @author quxinjun
     * @updateTime 2021/5/19 11:18
     */
    private void setBaseGroupInfoCache(BaseGroupInfoPO baseGroupInfoPO) {
        //添加群信息至缓存
        Redis.str.set(GroupKey.groupInfoKey(baseGroupInfoPO.getId()), JsonUtil.toJson(baseGroupInfoPO));
        Redis.key.setKeyTimeout(GroupKey.groupInfoKey(baseGroupInfoPO.getId()), BaseConstants.SAVE_TIME);
    }


    @Override
    public List<UnAuditVO> getUnAuditMemberList(Long uid, String groupId) {
        GroupUtils.validateIsManager(uid, groupId);
        List<UnAuditVO> list = userGroupMapper.findUnAuditList(groupId);
        for (UnAuditVO obj:list ) {
            obj.setUserId(McodeHandler.to32Radix(Long.parseLong(obj.getUserId())));
            obj.setInviteId(McodeHandler.to32Radix(Long.parseLong(obj.getInviteId())));
        }
        return list;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void exitGroupByGroupId(Long uid, String groupId) {
        BaseUserGroupPO entity = userGroupMapper.findUserGroupEntityByGroupId(groupId, uid);
        if (entity.getStatus() == 0) {
            SystemError.wrapBs(EnumType.ResponseCode.SUCCESS.getCode(), "您已被该群群主，或管理员踢出该群");
        }
        Set<String> members = Redis.zSet.range(GroupKey.groupMembersKey(groupId), 0, -1);
        //判断当前用户是不是群主 如果当前用户是群主，则自动转让群主至管理员  如果没有管理员则需要添加管理员
        if (EnumType.GroupIdentity.MASTER.getCode().equals(entity.getUserIdentity())) {
            String key = GroupKey.groupMembersKey(groupId);
            Set<String> range = Redis.zSet.range(key, 0, -1);
            //如果成员中只有一个人  直接解散该群
            if (range.size() == 1) {
                this.delGroup(uid, groupId);
                return;
            }
            Set<String> manager = Redis.zSet.rangeByScore(key,
                    EnumType.GroupIdentity.MANAGER.getCode().doubleValue(),
                    EnumType.GroupIdentity.MANAGER.getCode().doubleValue());
            if (manager == null || manager.isEmpty()) {
                SystemError.wrapBs(EnumType.ResponseCode.Fail.getCode(), "请先添加管理员，否则请解散该群");
            }
            //群主转移给管理员
            String memberId = manager.iterator().next();
            //修改缓存 原来群主的就不设置了  反正要删除
            userGroupMapper.updateEntityIdentity(groupId, String.valueOf(uid), EnumType.GroupIdentity.MEMBERS.getCode());
            userGroupMapper.updateEntityIdentity(groupId, memberId, EnumType.GroupIdentity.MASTER.getCode());
            Redis.zSet.add(key, memberId, EnumType.GroupIdentity.MASTER.getCode());
            Redis.zSet.remove(key, String.valueOf(uid));
            //发送消息
            GroupUtils.sendIds(new ArrayList<>(members), uid, groupId, EnumType.MessageCode.MESSAGE_GROUP_TURN.getCode(),
                    TipUtils.turnGroupTip(groupId, String.valueOf(uid)));
        }
        /*
            1. 删除组中成员信息
            2. 删除用户组消息中的组信息
             */
        userGroupMapper.updateEntityStatus(groupId, uid);
        Redis.zSet.remove(GroupKey.groupMembersKey(groupId), String.valueOf(uid));
        Redis.hash.delHash(UserKey.groupInfoKey(uid), groupId);
        members.remove(String.valueOf(uid));
        GroupUtils.sendIds(new ArrayList<>(members), uid, groupId, EnumType.MessageCode.MESSAGE_GROUP_EXIT.getCode(),
                TipUtils.groupExit(groupId, String.valueOf(uid)));
    }

    @Override
    public List<UnAuditVO> getMemberLeaveList(String groupId, Integer type, String searchKey) {
        List<UnAuditVO> list = userGroupMapper.getMemberLeaveList(groupId, type);
        List<UnAuditVO> list1 = new ArrayList<>();
        if (StringHandler.isNotEmpty(searchKey)) {
            for (UnAuditVO obj:list) {
                if(obj.getNickName().contains(searchKey)) {
                    obj.setUserId(McodeHandler.to32Radix(Long.parseLong(obj.getUserId())));
                    obj.setInviteId(McodeHandler.to32Radix(Long.parseLong(obj.getInviteId())));
                    list1.add(obj);
                }
            }
            return list1;
        }
        for (UnAuditVO obj:list) {
            obj.setUserId(McodeHandler.to32Radix(Long.parseLong(obj.getUserId())));
            obj.setInviteId(McodeHandler.to32Radix(Long.parseLong(obj.getInviteId())));
        }
        return list;
    }

    @Override
    public List<UserGroupVO> getCurrentGroupMembers(Long uid, String groupId, String searchKey) {
        List<UserGroupVO> listObj = userGroupMapper.getCurrentGroupMembers(uid, groupId);
        List<UserGroupVO> list1 = new ArrayList<>();
        if(StringHandler.isEmpty(listObj) || listObj.size()<=0){
            return list1;
        }
        List<UserGroupVO> list = listObj.stream().filter(o->
                (StringHandler.isNotEmpty(o.userId) && StringHandler.isNotEmpty(o.nickName))
        ).collect(Collectors.toList());
        if (StringHandler.isNotEmpty(searchKey)) {
            for (UserGroupVO obj:list) {
                if((obj.getNickName() != null && obj.getNickName().contains(searchKey))
                        || (obj.getRemarkName() != null && obj.getRemarkName().contains(searchKey))
                        || (obj.getUserGroupRemark() != null && obj.getUserGroupRemark().contains(searchKey))) {
                    if(StringHandler.isNotEmpty((obj.getUserId()))) {
                        obj.setUserId(McodeHandler.to32Radix(Long.parseLong(obj.getUserId())));
                        list1.add(obj);
                    }
                }
            }
            return list1;
        }
        for (UserGroupVO obj:list) {
            if(StringHandler.isNotEmpty((obj.getUserId())))
                obj.setUserId(McodeHandler.to32Radix(Long.parseLong(obj.getUserId())));
        }
        return list;
    }

    @Override
    public synchronized void joinGroup(Long uid, String groupId, String inviteId) {
        BaseGroupInfoPO cacheGroupInfo = GroupUtils.getCacheGroupInfo(groupId);
        validateGroupMemberLimit(groupId, 1, cacheGroupInfo.getMaxPerson());
        String key = GroupKey.groupMembersKey(groupId);
        long invite = McodeHandler.from32Radix(inviteId);
        //验证成员是否在该成员列表中
        Set<String> range = Redis.zSet.range(key, 0, -1);

        if (range == null || range.size() == 0) {
            SystemError.wrapBs(EnumType.ResponseCode.Fail.getCode(), "该群已解散");
        }
        if (range.contains(String.valueOf(uid))) {
            SystemError.wrapBs(EnumType.ResponseCode.Fail.getCode(), "您已在该群组中，无法重新添加");
        }

        Set<String> manager = Redis.zSet.rangeByScore(key, EnumType.GroupIdentity.MASTER.getCode().doubleValue(), EnumType.GroupIdentity.MANAGER.getCode().doubleValue());
        //如果说不是管理员  则需要去判断是否开启了入群申请功能
        String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern(BaseConstants.BASE_FORMAT));
        if (!manager.contains(String.valueOf(invite))) {
            BaseGroupInfoPO baseGroupInfoPO = this.getGroupBaseInfoByCache(groupId);
            if (baseGroupInfoPO.getIsAudit()) {
                //需要审核
                BaseUserGroupPO userGroupPO = new BaseUserGroupPO();
                userGroupPO.setUserId(String.valueOf(uid));
                userGroupPO.setGroupId(Long.parseLong(groupId));
                userGroupPO.setStatus(1);
                userGroupPO.setCreateTime(new Date());
                userGroupPO.setUserIdentity(EnumType.GroupIdentity.MEMBERS.getCode());
                userGroupPO.setInviteId(String.valueOf(invite));
                userGroupMapper.insertSelective(userGroupPO);
                //通知群主，管理员审核
                Set<String> members = Redis.zSet.rangeByScore(key, EnumType.GroupIdentity.MASTER.getCode().doubleValue(), EnumType.GroupIdentity.MANAGER.getCode().doubleValue());
//                UserUtils.sendIds(new ArrayList<>(members), uid, groupId, EnumType.MessageCode.MESSAGE_GROUP_ADD_MEMBER.getCode(), null);
                //组装数据
                MessageResponseBean response = UserUtils.getMessageResponse(String.valueOf(uid),
                        groupId, EnumType.MessageCode.MESSAGE_GROUP_ADD_MEMBER.getCode(),
                        "有新人入群需审核  去设置", time, 0.0, 0.0);

                members.forEach(obj -> GroupUtils.sendIfAbsent(response, obj, BaseConstants.GROUP_PREFIX + groupId));

                //通知用户
//                GroupUtils.sendIds(new ArrayList<>(Collections.singletonList(String.valueOf(uid))), uid, groupId,
//                        EnumType.MessageCode.MESSAGE_GROUP_JOIN_GROUP_NEED_VALIDATE.getCode(), "加入成功，等待管理员审核");

                SystemError.wrapBs("6002", "加入成功，等待管理员审核");
            }
        }
        //不需要审核
        BaseUserGroupPO userGroupPO = new BaseUserGroupPO();
        userGroupPO.setUserId(String.valueOf(uid));
        userGroupPO.setGroupId(Long.parseLong(groupId));
        userGroupPO.setCreateTime(new Date());
        userGroupPO.setStatus(1);
        userGroupPO.setUserIdentity(EnumType.GroupIdentity.MEMBERS.getCode());
        userGroupPO.setInviteId(String.valueOf(invite));
        userGroupPO.setAuditId(String.valueOf(invite));
        userGroupPO.setAuditStatus(1);
        userGroupPO.setAuditTime(time);
        userGroupMapper.insertSelective(userGroupPO);
        Redis.zSet.add(GroupKey.groupMembersKey(groupId), String.valueOf(uid), EnumType.GroupIdentity.MEMBERS.getCode());
//        Redis.key.setKeyTimeout(GroupKey.groupMembersKey(groupId), BaseConstants.SAVE_TIME);
        //获取成员信息
        Set<String> members = Redis.zSet.range(GroupKey.groupMembersKey(groupId), 0, -1);
        members.remove(String.valueOf(uid));
        ThreadUtil.execAsync(() -> {
            GroupUtils.sendIds(new ArrayList<>(members), uid, groupId, EnumType.MessageCode.MESSAGE_GROUP_INVITE_USER.getCode(),
                    TipUtils.invite(groupId, String.valueOf(uid), String.valueOf(invite)));
        });


        GroupUtils.sendIds(new ArrayList<>(Collections.singletonList(String.valueOf(uid))), uid, groupId, EnumType.MessageCode.MESSAGE_GROUP_INVITE_USER.getCode(),
                "您已加入群聊");
    }

    private BaseGroupInfoPO getGroupBaseInfoByCache(String groupId) {
        BaseGroupInfoPO baseGroupInfoPO = null;
        String groupKey = GroupKey.groupInfoKey(groupId);
        if (Redis.key.exists(groupKey)) {
            baseGroupInfoPO = JsonUtil.jsonToBean(Redis.str.get(groupKey), BaseGroupInfoPO.class);
        } else {
            baseGroupInfoPO = groupInfoMapper.selectByPrimaryKey(groupId);
        }
        return baseGroupInfoPO;
    }


    @Override
    public InactiveVO getInactiveList(Long uid, String groupId) {
        String groupMemberKey = GroupKey.groupMembersKey(groupId);
        String timeKey = UserKey.userLoginTimeByUid();
        InactiveVO inactiveVO = new InactiveVO();
        if (Redis.key.exists(groupMemberKey) && Redis.key.exists(timeKey)) {
            Set<String> members = Redis.zSet.range(groupMemberKey, 0, -1);
            //获取成员信息
            List<UserGroupVO> memberList = userGroupMapper.findUserGroupBaseInfoList(groupId, new ArrayList<>(members), uid);
            List<Object> values = Redis.hash.hmget(timeKey, new ArrayList<>(members));
            //其中任意一个集合没有数据   都返回空的对象
            if (memberList != null && !memberList.isEmpty() && values != null && !values.isEmpty()) {
                Map<String, String> map = values.stream().filter(Objects::nonNull).map(o -> (String) o).collect(Collectors.toMap(o -> o.split(":")[0], o -> o.split(":")[1]));
                //并行处理数据
                FutureTask<List<UserGroupVO>> primary = TaskUtils.run((t) -> this.getInactiveByTime(memberList, map, BaseConstants.GroupConstants.INACTIVE_LEVEL_ONE, BaseConstants.GroupConstants.INACTIVE_LEVEL_TWO));
                FutureTask<List<UserGroupVO>> general = TaskUtils.run((t) -> this.getInactiveByTime(memberList, map, BaseConstants.GroupConstants.INACTIVE_LEVEL_TWO, BaseConstants.GroupConstants.INACTIVE_LEVEL_THREE));
                FutureTask<List<UserGroupVO>> serious = TaskUtils.run((t) -> this.getInactiveByTime(memberList, map, BaseConstants.GroupConstants.INACTIVE_LEVEL_THREE, 0L));
                try {
                    //没有数据的人  默认都不活跃
                    List<UserGroupVO> collect = memberList.stream().filter(x -> !map.containsKey(x.getUserId())).map(x -> {
                        UserGroupVO userGroupVO = BeanUtil.copyProperties(x, UserGroupVO.class);
                        userGroupVO.setUserId(McodeHandler.to32Radix(Long.parseLong(userGroupVO.getUserId())));
                        return userGroupVO;
                    }).collect(Collectors.toList());
                    inactiveVO.setPrimary(primary.get());
                    inactiveVO.setGeneral(general.get());
                    List<UserGroupVO> userGroupVOS = serious.get();
                    userGroupVOS.addAll(collect);
                    inactiveVO.setSerious(userGroupVOS);
                } catch (InterruptedException | ExecutionException e) {
                    Logger.error("群不活跃成员获取异常", e);
                }
            }
        }
        return inactiveVO;
    }


    private List<UserGroupVO> getInactiveByTime(List<UserGroupVO> memberList, Map<String, String> map, long start, long end) {
        return memberList.stream()
                .filter(obj -> map.containsKey(obj.getUserId()))
                .filter(obj -> isInactiveByTime(map.get(obj.getUserId()), start, end))
                .map(x -> {
                    UserGroupVO userGroupVO = BeanUtil.copyProperties(x, UserGroupVO.class);
                    userGroupVO.setUserId(McodeHandler.to32Radix(Long.parseLong(userGroupVO.getUserId())));
                    return userGroupVO;
                })
                .collect(Collectors.toList());
    }

    private boolean isInactiveByTime(String s, long start, long end) {
        if (StringHandler.isEmpty(s)) {
            return true;
        }
        LocalDate lastTime = LocalDate.parse(s, DateTimeFormatter.ofPattern("yyyyMMdd"));
        long time = LocalDate.now().toEpochDay() - lastTime.toEpochDay();
        return end == 0L ? time >= start : (time >= start && time < end);
    }

    @Override
    public BaseUserFriendsPO queryMemberInfo(Long uid, String groupId, String memberId) {
        //判断是否开启群成员保护
        String key = GroupKey.groupInfoKey(groupId);
        BaseGroupInfoPO baseGroupInfoPO = null;
        if (!Redis.key.exists(key)) {
            String s = Redis.str.get(key);
            if (StringHandler.isNotEmpty(s)) {
                baseGroupInfoPO = JsonUtil.jsonToBean(s, BaseGroupInfoPO.class);
            } else {
                baseGroupInfoPO = groupInfoMapper.selectByPrimaryKey(groupId);
            }
        } else {
            baseGroupInfoPO = groupInfoMapper.selectByPrimaryKey(groupId);
        }
        if (ObjectUtil.isNull(baseGroupInfoPO)) {
            SystemError.wrapBs(EnumType.ResponseCode.Fail.getCode(), "数据出错");
        }

        //群主，或者管理员 即使开启了成员保护也能查看
        Set<String> range = Redis.zSet.rangeByScore(GroupKey.groupMembersKey(groupId), EnumType.GroupIdentity.MASTER.getCode().doubleValue(), EnumType.GroupIdentity.MANAGER.getCode().doubleValue());
        boolean flag = false;
        if (StringHandler.isNotEmpty(range) && !range.isEmpty()) {
            if (range.contains(String.valueOf(uid))) {
                flag = true;
            }
        }

        Double score = Redis.zSet.score(GroupKey.groupMembersKey(groupId), StringHandler.getString(McodeHandler.from32Radix(memberId)));
        if (StringHandler.isNotEmpty(score) && score == EnumType.GroupIdentity.MEMBERS.getCode().doubleValue()) {
            if (!flag && baseGroupInfoPO.getIsMemberProtect()) {
                SystemError.wrapBs(EnumType.ResponseCode.Fail.getCode(), "该群已开启群成员保护");
            }
        }

        BaseUserFriendsPO baseUserFriendsPO = userFriendsMapper.findMemberInfoByGroupId(groupId, McodeHandler.from32Radix(memberId), uid);
        if (StringHandler.isNotEmpty(baseUserFriendsPO)) {
            //判断是不是好友，是好友返回好友ID字段
            String o = (String) Redis.hash.hGet(UserKey.getUserFriendsListKey(uid), baseUserFriendsPO.getUserId());
            if (StringHandler.isNotEmpty(o)) {
                BaseUserFriendsPO cache = JsonUtil.jsonToBean(o, BaseUserFriendsPO.class);
                if (StringHandler.isNotEmpty(cache.getId())) {
                    baseUserFriendsPO.setFriendId(memberId);
                    //如果数据不一致   统一数据
                    if (StringHandler.isNotEmpty(cache)) {
                        if (!baseUserFriendsPO.getNickName().equals(cache.getNickName()) || !baseUserFriendsPO.getAvatarUrl().equals(cache.getAvatarUrl())) {
                            cache.setNickName(baseUserFriendsPO.getNickName());
                            cache.setAvatarUrl(baseUserFriendsPO.getAvatarUrl());

                            Redis.hash.hSet(UserKey.getUserFriendsListKey(uid), cache.getFriendId(), JsonUtil.toJson(cache));
                        }
                    }
                }
            }
            baseUserFriendsPO.setInviteId(McodeHandler.to32Radix(StringHandler.getLong(baseUserFriendsPO.getInviteId())));
            baseUserFriendsPO.setUserId(memberId);
        }
        return baseUserFriendsPO;
    }


    /**
     * 群人数限制
     *
     * @param groupId
     * @param currentSize
     * @return
     */
    private void validateGroupMemberLimit(String groupId, Integer currentSize, Integer memberSize) {
        if (StringHandler.isEmpty(memberSize)) {
            memberSize = StringHandler.getInt(Config.pp.getOrDefault(BaseConstants.DataSourceConstants.INIT_GROUP_SIZE, "400"));
        }
        //添加 邀请  加入 的人数超过 200
        if (currentSize >= memberSize) {
            SystemError.wrapBs(EnumType.ResponseCode.Fail.getCode(), "该群最多只能" + memberSize + "人");
        }
        //创建群的时候 如果人数超过200
        if (groupId == null) {
            if (currentSize >= memberSize) {
                SystemError.wrapBs(EnumType.ResponseCode.Fail.getCode(), "该群最多只能" + memberSize + "人");
            }
        } else {
            //邀请 加入的人数超过200
            Long size = Redis.zSet.size(GroupKey.groupMembersKey(groupId));
            if (size.intValue() == memberSize) {
                SystemError.wrapBs(EnumType.ResponseCode.Fail.getCode(), "当前群人数已满");
            }
            if ((size + currentSize) > memberSize) {
                SystemError.wrapBs(EnumType.ResponseCode.Fail.getCode(), "当前群人数已满");
            }
        }

    }


    @Override
    public GroupAssistantVO getGroupApiInfo(Long uid, String groupId) {
        GroupUtils.validateIsMaster(uid, groupId);
        String groupCode = McodeHandler.to32Radix(StringHandler.getLong(groupId));
        String name = UserUtils.getGroupCacheNameByGroupId(groupId);
        String token = Redis.str.get(GroupKey.groupAssistantToken(groupId));
        GroupAssistantVO groupAssistantVO = new GroupAssistantVO();
        groupAssistantVO.setGroupCode(groupCode);
        groupAssistantVO.setToken(token);
        groupAssistantVO.setName(name);
        return groupAssistantVO;
    }

    @Override
    public String resetGroupToken(Long uid, String groupId) {
        GroupUtils.validateIsMaster(uid, groupId);
        String groupAssistantTokenKey = GroupKey.groupAssistantToken(groupId);
        String token = IdUtil.randomUUID().replaceAll("-", "").toUpperCase();
        Redis.str.set(groupAssistantTokenKey, token);
        return token;
    }

    @Override
    public List<UserGroupVO> getUserAllGroupMembersInfo(Long uid) {
        List<UserGroupVO> list = userGroupMapper.getUserAllGroupMembersInfo(uid);
        if (list != null && !list.isEmpty()) {
            for (UserGroupVO obj:list) {
                if(StringHandler.isNotEmpty(obj.getUserId())){
                    obj.setUserId(McodeHandler.to32Radix(Long.parseLong(obj.getUserId())));
                }
            }
            return list;
        }
        return new ArrayList<>();
    }

    @Override
    public void addGroupMemberSize(String id, Integer memberSize) {
        BaseGroupInfoPO baseGroupInfoPO = groupInfoMapper.selectByPrimaryKey(id);
        if (StringHandler.isNotEmpty(baseGroupInfoPO)) {
            baseGroupInfoPO.setMaxPerson(memberSize);
            int i = groupInfoMapper.updateByPrimaryKeySelective(baseGroupInfoPO);
            if (i > 0) {
                Redis.str.set(GroupKey.groupInfoKey(id), JsonUtil.toJson(baseGroupInfoPO));
            }
        } else {
            SystemError.wrapBs("500", "数据出错！");
        }
    }

    @Override
    public void insertRecord(String groupId, Long uid) {
        groupInfoMapper.insertRecord(groupId, uid);
    }
    @Override
    public int userGroupNumber(long uid) {
        return groupInfoMapper.userGroupNumber(uid);
    }

    /**
     * @author 但何非
     * @dateTime 2023/2/1 11:26
     * @param uid:
     * @param groupId:
     * @param blackUid:
     * @return int
     * @Description:群用户拉黑接口
     */
    @Override
    public int groupUserBlack(long uid, String groupId, long blackUid){
        GroupUtils.validateIsManager(uid, groupId);
        return groupInfoMapper.groupUserBlack(uid,groupId,blackUid);
    }

    /**
     * @author 但何非
     * @dateTime 2023/2/1 11:26
     * @param uid:
     * @param groupId:
     * @param blackUid:
     * @return int
     * @Description:群用户解除拉黑接口
     */
    @Override
    public int groupUserBlackUn(long uid, String groupId, long blackUid) {
        GroupUtils.validateIsManager(uid, groupId);
        return groupInfoMapper.groupUserBlackUn(uid,groupId,blackUid);
    }

    /**
     * @author 但何非
     * @dateTime 2023/2/1 11:26
     * @param uid:
     * @param groupId:
     * @return int
     * @Description:用户是否拉黑
     */
    @Override
    public int groupUserBlackCount(long uid, String groupId) {
        return groupInfoMapper.groupUserBlackCount(uid,groupId);
    }
    @Override
    public int groupUserByidBlackCount(long uid) {
        return groupInfoMapper.groupUserByidBlackCount(uid);
    }


    /**
     * @author 但何非
     * @dateTime 2023/2/1 11:24
     * @param uid:
     * @param groupId:
     * @param searchKey:
     * @return java.util.List<com.ys.web.user.friends.vo.UnAuditVO>
     * @Description:群拉黑人员信息
     */
    @Override
    public List<UnAuditVO> groupUserBlackList(long uid,String groupId,String searchKey) {
        GroupUtils.validateIsManager(uid, groupId);
        List<UnAuditVO> list = userGroupMapper.groupUserBlackList(groupId);
        List<UnAuditVO> list1 = new ArrayList<>();
        if (StringHandler.isNotEmpty(searchKey)) {
            for (UnAuditVO obj:list) {
                if(obj.getNickName().contains(searchKey)) {
                    obj.setUserId(McodeHandler.to32Radix(Long.parseLong(obj.getUserId())));
                    list1.add(obj);
                }
            }
            return list1;
        }
        for (UnAuditVO obj:list) {
            obj.setUserId(McodeHandler.to32Radix(Long.parseLong(obj.getUserId())));
        }
        return list;
    }

    @Override
    public List<Long> userGroupId(String uid) {
        return userGroupMapper.userGroupId(uid);
    }

}
